extern crate libc;
use std::ffi::CString;

bitflags! {
    pub struct OpenFlags: i32 {
        const RDONLY = 0;
        const WRONLY = 1 << 0;
        const RDWR = 1 << 1;
        const CREATE = 1 << 6;
        const TRUNC = 1 << 9;
    }
}

pub fn fork() -> i32 {
    unsafe { libc::fork() }
}

pub fn getpid() -> i32 {
    unsafe { libc::getpid() }
}

pub fn execvp(path: &str, args: &[&str]) -> i32 {
    let path = CString::new(path).unwrap();
    let args: Vec<CString> = args.iter().map(|&s| CString::new(s).unwrap()).collect();

    let mut arg_ptrs: Vec::<*const i8> = vec![path.as_ptr()];
    for arg in args.iter() {
        arg_ptrs.push(arg.as_ptr());
    }
    arg_ptrs.push(std::ptr::null::<i8>());

    unsafe { libc::execvp(path.as_ptr(), arg_ptrs.as_ptr()) }
}

pub fn wait() -> (i32, i32) {
    let mut status = -1;
    let ret = unsafe { libc::wait(&mut status as *mut _) };
    (ret, status)
}

pub fn waitpid(pid: i32) -> (i32, i32) {
    let mut status = -1;
    let ret = unsafe { libc::waitpid(pid, &mut status as *mut _, 0) };
    (ret, status)
}

pub fn pipe() -> (i32, (i32, i32)) {
    let mut fds: [i32; 2] = [0; 2];
    let ptr = (&mut fds[0]) as *mut i32;
    let ret = unsafe { libc::pipe(ptr) };
    (ret, (fds[0], fds[1]))
}

pub fn open(path: &str, flag: OpenFlags) -> i32 {
    let path = CString::new(path).unwrap();
    unsafe { libc::open(path.as_ptr(), flag.bits) }
}

pub fn close(fd: i32) -> i32 {
    unsafe { libc::close(fd) }
}

pub fn dup(fd: i32) -> i32 {
    unsafe { libc::dup(fd) }
}